#include "texture.h"
#include "render.h"
#include "system.h"

#include <string.h>
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>

typedef struct _UFO_Texture_Internal
{
	IMAGE *image;
	ufoRect rect;
	TEXTUREMODE mode;

	UFO_Handle handle;
}UFO_Texture_Internal, *PUFO_Texture_Internal;

int ufoTextureDelete(UFO_Texture texture)
{
	ufoCloseHandle((UFO_Handle)texture);
	return 0;
}

int ufoTextureGetWidth(UFO_Texture texture)
{
	PUFO_Texture_Internal pTexture = ufoObjectFromHandle((UFO_Handle)texture);

	if (pTexture) {
		return (int)(pTexture->rect.right-pTexture->rect.left);
	}
	return 0;
}

int ufoTextureGetHeight(UFO_Texture texture)
{
	PUFO_Texture_Internal pTexture = ufoObjectFromHandle((UFO_Handle)texture);

	if (pTexture) {
		return (int)(pTexture->rect.bottom-pTexture->rect.top);
	}
	return 0;
}

UFO_Texture ufoTextureCreate(IMAGE *pImage, ufoRect *pSrcRect)
{
	PUFO_Texture_Internal pTexture = 0;
	pTexture = malloc(sizeof(UFO_Texture_Internal));
	memset(pTexture, 0, sizeof(UFO_Texture_Internal));

	pTexture->image = pImage;
	if (!pSrcRect) {
		pTexture->rect.right = (float)pImage->width;
		pTexture->rect.bottom = (float)pImage->height;
	}
	else {
		pTexture->rect = *pSrcRect;
	}

	if (pTexture->image->id == 0) {
		pTexture->image->id = ufoGenTextures(pImage->buffer, pImage->width, pImage->height, pImage->pixelSize);
	}

	pTexture->handle = ufoCreateHandle(UFO_ID_TEXTURE, pTexture);
	return (UFO_Texture)pTexture->handle;
}

int internalGet9Grid(const ufoRect *lprcTarget, const ufoRect *lprcGrid, ufoRect *lprc9)
{
	ufoRect lprc90 = {lprcTarget->left, lprcTarget->top , lprcTarget->left + lprcGrid->left, lprcTarget->top + lprcGrid->top};
	ufoRect lprc91 = {lprcTarget->left + lprcGrid->left, lprcTarget->top, lprcTarget->right - lprcGrid->right, lprcTarget->top + lprcGrid->top};
	ufoRect lprc92 = {lprcTarget->right - lprcGrid->right, lprcTarget->top, lprcTarget->right, lprcTarget->top + lprcGrid->top};

	ufoRect lprc93 = {lprcTarget->left, lprcTarget->top + lprcGrid->top, lprcTarget->left + lprcGrid->left, lprcTarget->bottom - lprcGrid->bottom};
	ufoRect lprc94 = {lprcTarget->left + lprcGrid->left, lprcTarget->top + lprcGrid->top, lprcTarget->right - lprcGrid->right, lprcTarget->bottom - lprcGrid->bottom};
	ufoRect lprc95 = {lprcTarget->right - lprcGrid->right, lprcTarget->top + lprcGrid->top, lprcTarget->right, lprcTarget->bottom - lprcGrid->bottom};

	ufoRect lprc96 = {lprcTarget->left, lprcTarget->bottom - lprcGrid->bottom, lprcTarget->left + lprcGrid->left, lprcTarget->bottom};
	ufoRect lprc97 = {lprcTarget->left + lprcGrid->left, lprcTarget->bottom - lprcGrid->bottom, lprcTarget->right - lprcGrid->right, lprcTarget->bottom};
	ufoRect lprc98 = {lprcTarget->right - lprcGrid->right, lprcTarget->bottom - lprcGrid->bottom, lprcTarget->right, lprcTarget->bottom};

	lprc9[0] = lprc90;
	lprc9[1] = lprc91;
	lprc9[2] = lprc92;
	lprc9[3] = lprc93;
	lprc9[4] = lprc94;
	lprc9[5] = lprc95;
	lprc9[6] = lprc96;
	lprc9[7] = lprc97;
	lprc9[8] = lprc98;

	return 1;
}

int ufoTextureDraw(UFO_Canvas canvas, UFO_Texture texture, HTEXTURE mask, const ufoRect *rectDst, float z, const ufoColor *color, float alpha, float dpi)
{
	z = 0.0f;//### not used

	if (!texture) {//### only update stenctil buffer
		//if (ufoGetStencilTest()){
			float x = (rectDst->left);
			float y = (-rectDst->top);
			float w = (rectDst->right-rectDst->left);
			float h = (rectDst->bottom-rectDst->top);

			float vertexBuffer[] = {
				(float)x,		(float)y-h,	(float)z,
				(float)x+w,	(float)y-h,	(float)z,
				(float)x,		(float)y,		(float)z,
				(float)x+w,	(float)y,		(float)z
			};

			if (mask) {
				HTEXTURE texs[2] = {0, mask};
				float textureWidth = (float)mask->width_POT;
				float textureHeight = (float)mask->height_POT;
				float dx = 0;
				float dy = mask->height;
				float dw = mask->width;
				float dh = mask->height;
				float texCoordBuffer[] = {
					(float)dx/(float)textureWidth,		(float)(dy-dh)/(float)textureHeight,
					(float)(dx+dw)/(float)textureWidth,	(float)(dy-dh)/(float)textureHeight,
					(float)dx/(float)textureWidth,		(float)dy/(float)textureHeight,
					(float)(dx+dw)/(float)textureWidth,	(float)dy/(float)textureHeight,
				};
				ufoRenderTexture(2, texs, 4, vertexBuffer, texCoordBuffer, color, alpha, dpi);	
			}
			else {
				ufoRenderTexture(0, 0, 4, vertexBuffer, 0, color, alpha, dpi);	
			}
		//}
	}
	else {

		PUFO_Texture_Internal pTexture = (PUFO_Texture_Internal)ufoObjectFromHandle((UFO_Handle)texture);
		switch(pTexture->mode.dwMode)
		{
		case TEXTUREMODE_STRETCH:
			{
				ufoRect *rectSrc = &pTexture->rect;

				float textureWidth = (float)pTexture->image->id->width_POT;
				float textureHeight = (float)pTexture->image->id->height_POT;
				float x = rectDst->left;
				float y = -rectDst->top;
				float w = rectDst->right-rectDst->left;
				float h = rectDst->bottom-rectDst->top;

				float dx = rectSrc->left;
				float dy = pTexture->image->id->height -rectSrc->top;
				float dw = rectSrc->right-rectSrc->left;
				float dh = rectSrc->bottom-rectSrc->top;

				float texCoordBuffer[] = {
					(float)dx/(float)textureWidth,		(float)(dy-dh)/(float)textureHeight,
					(float)(dx+dw)/(float)textureWidth,	(float)(dy-dh)/(float)textureHeight,
					(float)dx/(float)textureWidth,		(float)dy/(float)textureHeight,
					(float)(dx+dw)/(float)textureWidth,	(float)dy/(float)textureHeight,
				};

				float vertexBuffer[] = {
					(float)x,		(float)y-h,	(float)z/20.0f,
					(float)x+w,	(float)y-h,	(float)z/20.0f,
					(float)x,		(float)y,		(float)z/20.0f,
					(float)x+w,	(float)y,		(float)z/20.0f
				};

				if (mask) {
					HTEXTURE texs[2] = {pTexture->image->id, mask};
					ufoRenderTexture(2, texs, 4, vertexBuffer, texCoordBuffer, color, alpha, dpi);	
				}
				else { 
					ufoRenderTexture(1, &pTexture->image->id, 4, vertexBuffer, texCoordBuffer, color, alpha, dpi);
				}
			}
			break;

		case TEXTUREMODE_CENTER:
			{
				ufoRect *rectSrc = &pTexture->rect;
				float textureWidth = (float)pTexture->image->id->width_POT;
				float textureHeight = (float)pTexture->image->id->height_POT;

				float dx = rectSrc->left;
				float dy = pTexture->image->id->height -rectSrc->top;
				float dw = rectSrc->right-rectSrc->left;
				float dh = rectSrc->bottom-rectSrc->top;				
								
				float w = pTexture->mode.destRect.right - pTexture->mode.destRect.left;
				float h = pTexture->mode.destRect.bottom - pTexture->mode.destRect.top;
				float x = rectDst->left + ((rectDst->right-rectDst->left)-w)/2;
				float y = -rectDst->top - ((rectDst->bottom-rectDst->top)-h)/2;

				float texCoordBuffer[] = {
					(float)dx/(float)textureWidth,		(float)(dy-dh)/(float)textureHeight,
					(float)(dx+dw)/(float)textureWidth,	(float)(dy-dh)/(float)textureHeight,
					(float)dx/(float)textureWidth,		(float)dy/(float)textureHeight,
					(float)(dx+dw)/(float)textureWidth,	(float)dy/(float)textureHeight,
				};

				float vertexBuffer[] = {
					(float)x,		(float)y-h,	(float)z/20.0f,
					(float)x+w,	(float)y-h,	(float)z/20.0f,
					(float)x,		(float)y,		(float)z/20.0f,
					(float)x+w,	(float)y,		(float)z/20.0f
				};

				ufoRenderTexture(1, &pTexture->image->id, 4, vertexBuffer, texCoordBuffer, color, alpha, dpi);
			}
			break;

		case TEXTUREMODE_9GRID:
			{
				int i = 0;
				ufoRect rc9GridTarget[9];
				ufoRect rc9GridTexture[9];

				float *pVertex = (float*)malloc(3*4*9*sizeof(float));
				float *pTex = (float*)malloc(2*4*9*sizeof(float));
				
				internalGet9Grid(rectDst, &pTexture->mode.destRect, rc9GridTarget);
				internalGet9Grid(&pTexture->rect, &pTexture->mode.rect, rc9GridTexture);

				for (i = 0; i < 9; ++i)
				{
					ufoRect *rectSrc = &rc9GridTexture[i];

					float textureWidth = (float)pTexture->image->id->width_POT;
					float textureHeight = (float)pTexture->image->id->height_POT;

					float x = rc9GridTarget[i].left;
					float y = -rc9GridTarget[i].top;
					float w = rc9GridTarget[i].right-rc9GridTarget[i].left;
					float h = rc9GridTarget[i].bottom-rc9GridTarget[i].top;

					float dx = rectSrc->left;
					float dy = pTexture->image->id->height -rectSrc->top;
					float dw = rectSrc->right-rectSrc->left;
					float dh = rectSrc->bottom-rectSrc->top;

					float texCoordBuffer[] = {
						(float)dx/(float)textureWidth,		(float)(dy-dh)/(float)textureHeight,
						(float)(dx+dw)/(float)textureWidth,	(float)(dy-dh)/(float)textureHeight,
						(float)dx/(float)textureWidth,		(float)dy/(float)textureHeight,
						(float)(dx+dw)/(float)textureWidth,	(float)dy/(float)textureHeight,
					};

					float vertexBuffer[] = {
						(float)x,		(float)y-h,	(float)z/20.0f,
						(float)x+w,	(float)y-h,	(float)z/20.0f,
						(float)x,		(float)y,		(float)z/20.0f,
						(float)x+w,	(float)y,		(float)z/20.0f
					};

					memcpy(pVertex+i*3*4, vertexBuffer, 3*4*sizeof(float));
					memcpy(pTex+i*2*4, texCoordBuffer, 2*4*sizeof(float));
				}

				ufoRenderTexture(1, &pTexture->image->id, 4*9, pVertex, pTex, color, alpha, dpi);

				free(pVertex);
				free(pTex);
			}
			break;
		}

		return 1;
	}

	return 0;
}

int ufoTextureSetMode(UFO_Texture texture, TEXTUREMODE *mode)
{
	PUFO_Texture_Internal pTexture = ufoObjectFromHandle((UFO_Handle)texture);
	
	if (pTexture && mode) {
		pTexture->mode = *mode;
		return 1;
	}

	return 0;
}

int ufoTextureLock( UFO_Texture texture, LPTEXTURE_LOCKED_RECT pLockRect, ufoRect *pRect )
{
	PUFO_Texture_Internal pTexture = ufoObjectFromHandle((UFO_Handle)texture);
	if (pTexture && pTexture->image && pLockRect) {
		if (!pRect)
			pRect = &pTexture->rect;

		//### yuedongweng not finsh, just for waterview
		pLockRect->pBits = pTexture->image->buffer;
		pLockRect->Pitch = pTexture->image->width*pTexture->image->pixelSize;

		return 1;
	}
	return 0;
}

int ufoTextureUnLock( UFO_Texture texture )
{
	PUFO_Texture_Internal pTexture = ufoObjectFromHandle((UFO_Handle)texture);
	if (pTexture) {
		//### do something for mutil-thread
		return 1;
	}
	return 0;
}
